-------------------------------------------------------------------------------
-- Floating Point Adder
-- ECEn 620
-- Fall 2008
-- Jonathan Johnson, James Carroll
-------------------------------------------------------------------------------

library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_signed.all;
use ieee.numeric_std.all;

entity fp_adder is

  port (
    operand0 : in std_logic_vector(9 downto 0);
    operand1 : in std_logic_vector(9 downto 0);
    operation : in std_logic;

    result : out std_logic_vector(9 downto 0)
    );

end entity fp_adder;

architecture behavioral of fp_adder is

  -- the operand sign bits
  signal sign0 : std_logic;
  signal sign1 : std_logic;
  signal sign1_prime : std_logic;
  
  -- the operand exponent bits
  signal exp0 : std_logic_vector(3 downto 0);
  signal exp1 : std_logic_vector(3 downto 0);

  -- allocate 6 bits for the mantissas since we are
  -- adding in the implied 1's
  signal mantissa0 : std_logic_vector(5 downto 0);
  signal mantissa1 : std_logic_vector(5 downto 0);

  signal mantissa0_swapped : std_logic_vector(5 downto 0);
  signal mantissa1_swapped : std_logic_vector(5 downto 0);
  
  signal sign0_swapped : std_logic;
  signal sign1_swapped : std_logic;

  signal mantissa1_rshifted : std_logic_vector(8 downto 0);
  -- difference between the two operand exponents (exp1 - exp0)
  signal exponent_difference : std_logic_vector(4 downto 0);
  signal exponent_difference_magnitude : std_logic_vector(4 downto 0);
  signal exponents_equal : std_logic;
  
  -- difference between the two operand mantissas (mantissa1 - mantissa0)
  signal mantissa_difference : std_logic_vector(6 downto 0);

-------------------------------------------------------------------------------
-- Comparison Logic
-------------------------------------------------------------------------------
  signal s0GTEs1 : std_logic;
  signal swap : std_logic;

-------------------------------------------------------------------------------
-- Sign Logic
-------------------------------------------------------------------------------
  signal sign_inputs : std_logic_vector(2 downto 0);
  signal sign_out : std_logic;

-------------------------------------------------------------------------------
-- Adder / signed magnitude / 2's complement conversion
-------------------------------------------------------------------------------
  signal mantissa0_2c : std_logic_vector(9 downto 0);
  signal mantissa1_2c : std_logic_vector(9 downto 0);
  signal sum_2c : std_logic_vector(10 downto 0);
  signal sum_2c_comp : std_logic_vector(10 downto 0);
  signal sum_mag : std_logic_vector(9 downto 0);
  signal sum_sign : std_logic;

-----------------------------------------------------------------------------
-- Normalization
-----------------------------------------------------------------------------
  signal shift_amount : std_logic_vector(3 downto 0); -- possible values will
                                                      -- range from -8 to +1
  signal sum_mag_shifted : std_logic_vector(8 downto 0);
  
-------------------------------------------------------------------------------
-- Exponent
-------------------------------------------------------------------------------
  signal exp_mux : std_logic_vector(3 downto 0);
  signal exp_normalize : std_logic_vector(4 downto 0);
  signal exp_round : std_logic_vector(4 downto 0);
  signal exp_normalize_carry : std_logic;
  signal exp_round_carry : std_logic;
  signal underflow : std_logic;
  signal overflow : std_logic;
-------------------------------------------------------------------------------
-- Rounding
-------------------------------------------------------------------------------
  signal rounding_r : std_logic;
  signal rounding_s : std_logic;
  signal rounding_lsb : std_logic;
  signal rounding_rnd : std_logic;
  signal sum_mag_rounded : std_logic_vector(6 downto 0);
  signal sum_mag_rounded_normal : std_logic_vector(5 downto 0);

-------------------------------------------------------------------------------
-- Output
-------------------------------------------------------------------------------
  signal result_exp : std_logic_vector(3 downto 0);
  signal result_mantissa :  std_logic_vector(4 downto 0);
  signal result_exp_mantissa : std_logic_vector(8 downto 0);
      
-------------------------------------------------------------------------------
-- Special Cases
-------------------------------------------------------------------------------
  signal infinity0 : std_logic; -- is operand 0 an infinity?
  signal infinity1 : std_logic; -- is operand 1 an infinity?
  signal infinities : std_logic; -- are both operands infinity?
  signal zero0     : std_logic; -- is operand 0 a zero?
  signal zero1     : std_logic; -- is operand 1 a zero?
  signal zeros    : std_logic; -- are both operands zeros?

begin

  sign0 <= operand0(9);
  sign1 <= operand1(9);
  sign1_prime <= sign1 xor operation;
  
  exp0 <= operand0(8 downto 5);
  exp1 <= operand1(8 downto 5);

  mantissa0 <= '1' & operand0(4 downto 0) when (exp0 /= "0000" and exp0 /= "1111") else '0' & operand0(4 downto 0);  -- add the implied 1 when needed
  mantissa1 <= '1' & operand1(4 downto 0) when (exp1 /= "0000" and exp1 /= "1111") else '0' & operand1(4 downto 0);  -- add the implied 1 when needed

  -- these signals are for convenience when taking care of special cases later
  -- infinity0 is high when the first operand is an infinity
  -- infinity1 is high when the second operand is an infinity
  -- infinities is high when both operands are infinities
  infinity0 <= '1' when (exp0 = "1111" and mantissa0 = "000000") else '0';
  infinity1 <= '1' when (exp1 = "1111" and mantissa1 = "000000") else '0';
  infinities <= infinity0 and infinity1;

  -- these signals are also for convenience when taking care of special cases later
  -- zero0 is high when the first operand is a zero
  -- zero1 is high when the second operand is a zero
  -- zeros is high when both operands are zeros
  zero0 <= '1' when (exp0 = "0000" and mantissa0 = "000000") else '0';
  zero1 <= '1' when (exp1 = "0000" and mantissa1 = "000000") else '0';
  zeros <= zero0 and zero1;

  -- the exponents are interpreted as unsigned by adding the leading zero
  -- before subtracting them
  exponent_difference <= ('0' & exp0) - ('0' & exp1);
  exponent_difference_magnitude <= exponent_difference when exponent_difference(4) = '0' else (not exponent_difference) + 1;
  mantissa_difference <= ('0' & mantissa0) - ('0' & mantissa1);

  -- Determine when operand 0 is greater than or equal to operand 1
  -- This occurs when the difference between the exponents is positive and
  -- also when the difference between the exponents is 0 AND the difference
  -- between the mantissas is 0 or positive
  exponents_equal <= '1' when exp0 = exp1 else '0';
  s0GTEs1 <= '1' when (exponent_difference(4) = '0' and exponents_equal /= '1') or (exponents_equal = '1' and mantissa_difference(6) = '0') else '0';
  
  -- Determine when to swap the mantissas
  swap <= not s0GTEs1;

  -- swap mantissas if needed
  mantissa0_swapped <= mantissa0 when swap = '0' else mantissa1;
  mantissa1_swapped <= mantissa1 when swap = '0' else mantissa0;
  sign0_swapped <= sign0 when swap = '0' else sign1_prime;
  sign1_swapped <= sign1_prime when swap = '0' else sign0;
  
  -- shift mantissa1_swapped right if needed (one line barrel shifter with sticky
  -- bit consideration)
  mantissa1_rshifted <= mantissa1_swapped & "000" when exponent_difference_magnitude = "00000" else
                        '0' & mantissa1_swapped & "00" when exponent_difference_magnitude = "00001" else
                        "00" & mantissa1_swapped & "0" when exponent_difference_magnitude = "00010" else
                        "000" & mantissa1_swapped when exponent_difference_magnitude = "00011" else
                        "0000" & mantissa1_swapped(5 downto 2) & (mantissa1_swapped(1) or mantissa1_swapped(0)) when exponent_difference_magnitude = "00100" else
                        "00000" & mantissa1_swapped(5 downto 3) & (mantissa1_swapped(2) or mantissa1_swapped(1) or mantissa1_swapped(0)) when exponent_difference_magnitude = "00101" else
                        "000000" & mantissa1_swapped(5 downto 4) & (mantissa1_swapped(3) or mantissa1_swapped(2) or mantissa1_swapped(1) or mantissa1_swapped(0)) when exponent_difference_magnitude = "00110" else
                        "0000000" & mantissa1_swapped(5) & (mantissa1_swapped(4) or mantissa1_swapped(3) or mantissa1_swapped(2) or mantissa1_swapped(1) or mantissa1_swapped(0)) when exponent_difference_magnitude = "00111" else
                        "00000000" & (mantissa1_swapped(5) or mantissa1_swapped(4) or mantissa1_swapped(3) or mantissa1_swapped(2) or mantissa1_swapped(1) or mantissa1_swapped(0));
  
  sign_inputs <= sign0 & sign1_prime & s0GTEs1;

  -- first, the special cases for zeros and infinties are considere
  -- then, the general logic is applied (last line of the statement)
  sign_out <= '0' when (operation = '0' and zeros = '1' and (sign0 = '0' or sign1 = '0')) or
                       (operation = '1' and zeros = '1' and (sign0 = '0' or sign1 = '1')) or
                       (operation = '0' and ((infinity0 = '1' and sign0 = '0') or (infinity1 = '1' and sign1 = '0'))) or
                       (operation = '1' and ((infinity0 = '1' and sign0 = '0') or (infinity1 = '1' and sign1 = '1'))) else                       
              '1' when (operation = '0' and zeros = '1' and (sign0 = '1' and sign1 = '1')) or
                       (operation = '1' and zeros = '1' and (sign0 = '1' and sign1 = '0')) or
                       (operation = '0' and ((infinity0 = '1' and sign0 = '1') or (infinity1 = '1' and sign1 = '1'))) or
                       (operation = '1' and ((infinity0 = '1' and sign0 = '1') or (infinity1 = '1' and sign1 = '0'))) else
              '0' when (sum_2c = "00000000000") else
              '0' when sign_inputs(2 downto 1) = "00" or sign_inputs = "011" or sign_inputs = "100" else '1';

  mantissa0_2c <= '0' & mantissa0_swapped & "000" when sign0_swapped = '0' else ((not ('0' & mantissa0_swapped)) + "0000001") & "000";
  mantissa1_2c <= '0' & mantissa1_rshifted when sign1_swapped = '0' else (not ('0' & mantissa1_rshifted)) + "0000000001";

  sum_2c <= (mantissa0_2c(9) & mantissa0_2c) + mantissa1_2c;
  sum_2c_comp <= (not sum_2c) + 1;
  sum_sign <= sum_2c(10);
  sum_mag <= sum_2c(9 downto 0) when sum_sign = '0' else sum_2c_comp(9 downto 0);

  shift_amount <= std_logic_vector(to_signed(0, 4)) when sum_mag(9 downto 0) = "0000000000" else
                  std_logic_vector(to_signed(1, 4)) when sum_mag(9) = '1' else
                  std_logic_vector(to_signed(0, 4)) when sum_mag(9 downto 8) = "01" else
                  std_logic_vector(to_signed(-1, 4)) when sum_mag(9 downto 7) = "001" else
                  std_logic_vector(to_signed(-2, 4)) when sum_mag(9 downto 6) = "0001" else
                  std_logic_vector(to_signed(-3, 4)) when sum_mag(9 downto 5) = "00001" else
                  std_logic_vector(to_signed(-4, 4)) when sum_mag(9 downto 4) = "000001" else
                  std_logic_vector(to_signed(-5, 4)) when sum_mag(9 downto 3) = "0000001" else
                  std_logic_vector(to_signed(-6, 4)) when sum_mag(9 downto 2) = "00000001" else
                  std_logic_vector(to_signed(-7, 4)) when sum_mag(9 downto 1) = "000000001" else
                  std_logic_vector(to_signed(-8, 4));

  sum_mag_shifted <= sum_mag(9 downto 2) & (sum_mag(1) or sum_mag(0)) when shift_amount = 1 else
                     sum_mag(8 downto 0) when shift_amount = 0 else
                     sum_mag(7 downto 0) & '0' when shift_amount = -1 else
                     sum_mag(6 downto 0) & "00" when shift_amount = -2 else
                     sum_mag(5 downto 0) & "000" when shift_amount = -3 else
                     sum_mag(4 downto 0) & "0000" when shift_amount = -4 else
                     sum_mag(3 downto 0) & "00000" when shift_amount = -5 else
                     sum_mag(2 downto 0) & "000000" when shift_amount = -6 else
                     sum_mag(1 downto 0) & "0000000" when shift_amount = -7 else
                     sum_mag(0) & "00000000";

  exp_mux <= exp0 when s0GTEs1 = '1' else exp1;
  exp_normalize <= ('0' & exp_mux) + shift_amount;
  exp_normalize_carry <= '1' when exp_normalize(4) = '1' or exp_normalize(3 downto 0) = "1111" else '0';
  
  rounding_r <= sum_mag_shifted(2);
  rounding_s <= sum_mag_shifted(0) or sum_mag_shifted(1);
  rounding_lsb <= sum_mag_shifted(3);
  rounding_rnd <= '1' when (rounding_r = '1' and rounding_s = '1') or (rounding_lsb = '1' and rounding_r = '1' and rounding_s = '0') else '0';
  sum_mag_rounded <= ('0' & sum_mag_shifted(8 downto 3)) + rounding_rnd;
  sum_mag_rounded_normal <= sum_mag_rounded(5 downto 0) when sum_mag_rounded(6) = '0' else
                            sum_mag_rounded(6 downto 1);
  exp_round <= exp_normalize + sum_mag_rounded(6);
  exp_round_carry <= '1' when exp_round(4) = '1' or exp_round(3 downto 0) = "1111" else '0';
  
  result_exp <= exp_round(3 downto 0);
  result_mantissa <= sum_mag_rounded_normal(4 downto 0);

  overflow <= '1' when (exp_normalize_carry = '1' and shift_amount = 1) or (exp_round_carry = '1' and exp_normalize_carry = '0') else '0';
  underflow <= '1' when (exp_normalize_carry = '1' and shift_amount /= 1) or exp_round = "00000" else '0';
  
  -- generate NaN when adding opposite infities or subtracting the same infinities
  -- generate infinity when an exponent overflow happens or either input is an infinity (except for when a NaN is needed)
  -- generate 0 when the addition of the mantissas resulted in 0
  -- otherwise, use the normalized exponent and mantissa  
  result_exp_mantissa <= "111111111" when (operation = '0' and ((infinities = '1' and sign0 /= sign1))) or (operation = '1' and ((infinities = '1' and sign0 = sign1))) else
                         "111100000" when overflow = '1' or infinity0 = '1' or infinity1 = '1' else
                         "000000000" when sum_2c = "00000000000" or underflow = '1' else
                         (result_exp & result_mantissa);

  result <= sign_out & result_exp_mantissa;
  
end behavioral;
